package pyip.lib.javalearn.io.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class NioTcpClient implements Runnable {

    private SocketChannel socketChannel;
    private String host;
    private int port;

    private CharsetDecoder decoder = null;
    private ByteBuffer readBuf = null;
    private MessageProvider messageProvider = null;
    private Thread messageProviderThread = null;

    private static final String CHARSET_UTF8 = "utf-8";

    public NioTcpClient(String host, int port) throws IOException {
        this.host = host;
        this.port = port;
        socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);

        decoder = Charset.forName(CHARSET_UTF8).newDecoder();
        readBuf = ByteBuffer.allocate(512);
        messageProvider = new MessageProvider();
        messageProviderThread = new Thread(messageProvider);
    }

    public void stop() {
        messageProvider.stop();
    }

    @Override
    public void run() {
        // 启动线程获取键盘输入
        // TODO 获取键盘输入阻塞时, 无法唤醒, 导致无法结束线程
        messageProviderThread.start();

        try {
            socketChannel.connect(new InetSocketAddress(host, port));
            while (!socketChannel.finishConnect()) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // ignore
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            stop();
            return;
        }

        System.out.println("TCPClient connected");

        while (true) {
            try {
                List<String> msgList = messageProvider.getMessage();
                if (msgList != null) {
                    for (String message : msgList) {
                        if (message.equalsIgnoreCase("!!q")) {
                            stop();
                            return;
                        }

                        socketChannel.socket().sendUrgentData(0xAAAA);
                        System.out.println("me: urgentData 0xAAAA");
//                        write(message);
//                        System.out.println("me: " + message);

                        String receiveMsg = read();
                        if (receiveMsg != null && receiveMsg.length() != 0) {
                            System.out.println("server: " + receiveMsg);
                        }
                    }
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // ignore
            }
        }
    }

    private String read() throws IOException {
        readBuf.clear();
        socketChannel.read(readBuf);
        readBuf.flip();
        return decoder.decode(readBuf).toString();
    }

    private void write(String message) throws IOException {
        if (message == null || message.length() == 0) {
            return;
        }
        byte[] bytes = message.getBytes(CHARSET_UTF8);
        ByteBuffer buf = ByteBuffer.allocate(bytes.length).put(bytes);
        buf.flip();
        while (buf.hasRemaining()) {
            socketChannel.write(buf);
        }
    }

    public static void main(String[] args) {
        try {
            NioTcpClient client = new NioTcpClient("127.0.0.1", 9999);
            new Thread(client).start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class MessageProvider implements Runnable {

    private Scanner scanner = new Scanner(System.in);
    private List<String> msgList = new ArrayList<>();
    private boolean stop = false;

    @Override
    public void run() {
        while (!stop) {
            try {
                String line = scanner.nextLine();
                synchronized (msgList) {
                    msgList.add(line);
                }
            } catch (IllegalStateException e) {
                // ignore, should not happen
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void stop() {
        stop = true;
        scanner.close();
    }

    public List<String> getMessage() {
        if (msgList.size() > 0) {
            synchronized (msgList) {
                List<String> temp = new ArrayList<>(msgList.size());
                temp.addAll(msgList);
                msgList.clear();
                return temp;
            }
        }
        return null;
    }
}